package gameStates.absGamesStates;

import java.util.logging.Logger;

import input.PlayerKeyInput;
import input.PlayerMouseInput;

import com.jme.input.InputHandler;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.Renderer;
import com.jme.scene.CameraNode;
import com.jme.scene.Node;
import com.jme.scene.Spatial.CullHint;
import com.jme.scene.state.LightState;
import com.jme.util.Timer;

import logic.common.game.Game;
import logic.common.player.Player;
import logic.nodes.TeamNode;
import logic.nodes.collision.CollidableNode;
import logic.nodes.lod.blocks.Block;
import logic.nodes.lod.blocks.LeafBlock;
import logic.ships.hunter.Hunter;
import main.listeners.ListenerContainer;
import main.listeners.ListenerKeeper;
import main.threading.ThreadPool;
import map.Map;
import gameStates.GameTimeListener;
import gameStates.PassGameState;
import gameStates.absGamesStates.gui.AbsChatState;
import gameStates.absGamesStates.gui.AbsHUDState;
import gameStates.absGamesStates.gui.AbsSpawnState;
import gameStates.passes.AbsPassManagerState;

public abstract class AbsIngameState extends PassGameState implements ListenerKeeper {
	
	public static final Logger logger = Logger.getLogger("gameStates.absGamesStates.AbsIngameState");
	
	protected Node occluders;
	/** The {@link Camera} that is used in this IngameState. */
	protected Camera cam;
	/** The {@link Timer} that is used for time-measurement. */
	protected Timer timer;
	/** The {@link Map} to be processed. */
	protected Map map;
	/** Indicates if this IngameState has already been initialized. */
	protected boolean initialized;
	/** Contains all {@link TeamNode}s of this State. */
	protected Block rootBlock;
	protected LeafBlock outerBlock;
	protected LightState lightState;
	
	/** The {@link Game} that is processed in this MatchState. */
	protected Game game;
	
	/** The main-{@link InputHandler} for this MatchState. */
	protected InputHandler input;
	/** The key-input-handler for the {@link Player}. */
	protected PlayerKeyInput playerKeyInput;
	/** The mouse-input-handler for the {@link Player}. */
	protected PlayerMouseInput playerMouseInput;
	
	/** The {@link Player} that plays in this MatchState. */
	protected Player player;
	
	/** The {@ link CameraNode} that is used for {@link Camera}-movement. */
	protected CameraNode camNode;
	
	/** The current time in seconds until the {@link Game}, that just ended, will reload to the next
	 * {@link Map} in the {@link GameCycle}. */
	protected int currentCoundDownTime;
	
	protected int currentSeconds;
	
	protected AbsHUDState hudState;
	protected AbsSpawnState spawnState;
	protected AbsChatState chatState;
	
	protected ThreadPool threadPool;
	
	protected ListenerContainer gameTimeListeners;
	
	protected boolean isPaused;
	
	public AbsIngameState(AbsPassManagerState passManagerState) {
		super("ingame", passManagerState);
		
		rootNode.setCullHint(CullHint.Dynamic);
		rootNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
		
		occluders = new Node("Occluders");
		rootNode.attachChild(occluders);
		
		input = new InputHandler();

		threadPool = new ThreadPool();
		
		outerBlock = new LeafBlock(Vector3f.ZERO, Float.POSITIVE_INFINITY, this);
		
		gameTimeListeners = new ListenerContainer(GameTimeListener.class);
	}
	
	public ThreadPool getThreadPool() { return threadPool; }
	
	public AbsHUDState getHUDState() { return hudState; }
	
	public int getCurrentSeconds() { return currentSeconds; }
	
	public void addGameTimeListener(GameTimeListener l) {
		gameTimeListeners.addListener(l);
	}
	
	public void removeGameTimeListener(GameTimeListener l) {
		gameTimeListeners.removeListener(l);
	}
	
	public void updateGameTime(int seconds) {
		currentSeconds = seconds;
		
		gameTimeListeners.callMethod(GameTimeListener.GAMETIMEUPDATE, Integer.valueOf(currentSeconds));
	}
	
	public void updateRespawnTime() {		
		gameTimeListeners.callMethod(GameTimeListener.RESPAWNTIMEUPDATE);
	}
	
	public void updateEndTime(int endTime) {
		currentCoundDownTime = endTime;
		
		gameTimeListeners.callMethod(GameTimeListener.ENDTIMEUPDATE, Integer.valueOf(currentCoundDownTime));
	}
	
	protected void clearInput() {
		input.clearActions();
		input.removeFromAttachedHandlers(playerKeyInput);
		input.removeFromAttachedHandlers(playerMouseInput);
		rootNode.clearControllers();
		if(player != null) {
			Hunter hunter = player.getHunter();
			if(hunter != null) hunter.removeHunterFlightControllers();
		}
	}
	
	/**
	 * Sets this MatchState active or not. If it is already initialized, the input and the mouse are 
	 * effected as well.
	 */
	@Override
	public void setActive(boolean active) {
		super.setActive(active);
		if(!initialized) return;
		input.setEnabled(active);
	}
	
	/**
	 * Enables/disables the main-input-handler.
	 * @param enable true, if the main-input-handler shall be activated. false, else.
	 */
	@Override
	public void enableInput(boolean enable) {
		super.enableInput(enable);
		if(input != null) input.setEnabled(enable);
	}
	
	/**
	 * Returns the current {@link Player} of the current {@link Game}.
	 * @return The current {@link Player}.
	 */
	public Player getPlayer() { return player; }
	
	/**
	 * Returns the {@link Game} processed by this MatchState.
	 * @return The {@link Game}.
	 */
	public Game getGame() { return game; }
	
	public Node getOccluders() { return occluders; }
	
	/**
	 * Returns, if this IngameState has been initialized or not.
	 * @return true, of this IngameState has been initialized. false, if not.
	 */
	public boolean isInitialized() { return initialized; }	
		
	/**
	 * Returns the {@link Camera} of this IngameState.
	 * @return The {@link Camera}.
	 */
	public Camera getCam() { return cam; }
	
	public void setGame(Game game) {
		this.game = game;
		
		currentCoundDownTime = game.getCoundDown();
	}
	
	public LightState getLightState() { return lightState; }
	
	public Block getRootBlock() { return rootBlock; }
	
	public LeafBlock getOuterLeafBlock() { return outerBlock; }
	
	public AbsSpawnState getSpawnState() { return spawnState; }
	
	public void setSpawnMenuState(AbsSpawnState spawnState) { this.spawnState = spawnState; }
	
	public boolean isPaused() { return isPaused; }
	
	public void setPaused(boolean pause) { isPaused = pause; }
	
	@Override
	public ListenerContainer getListenerContainer(Class<?> listenerClass) {
		if(listenerClass == GameTimeListener.class) return gameTimeListeners;
		return null;
	}
	
	public void setRootBlock(Block rootBlock) {
		this.rootBlock = rootBlock;
	}
	
	@Override
	public void cleanup() {
		super.cleanup();
		
		threadPool.cleanup();
		
		chatState = null;
		hudState = null;
		spawnState = null;
		
		input.clearActions();
		input = null;
		
		camNode = null;
		game = null;
		map = null;
		
		occluders.detachAllChildren();
		player.cleanUp();
		player = null;
		
		rootBlock = null;
		outerBlock = null;
	}
	
	public abstract void initNewMap(Map map);
			
	public abstract void initPlayerKeyInput();
	
	public abstract void addNode(CollidableNode node);
	
	public abstract void initIngameWindows();
		
	public abstract void setupCam();
	
	public abstract void setPlayer(Player player);
		
	public abstract CameraNode getCamNode();
	
	public abstract boolean isInRootBlock(CollidableNode node);
	
	public abstract void spawnCurrentPlayer();

	public abstract void setCamNode(Hunter hunter);
}